package cz.vse.javaee.album;

import cz.vse.javaee.album.AlbumDAO;
import cz.vse.javaee.album.Photo;

import javax.ejb.EJB;
import javax.ejb.EJBs;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.MultipartConfig;
import javax.servlet.http.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.text.ParseException;

/**
 * MultipartConfig anotace zpřístupní metody pro
 * práci s formulářem, který odesílá dotaz složený
 * z více částí (MIME multipart/form-data).
 */
@MultipartConfig
@EJBs({
        @EJB(beanName = "PhotoGateway",
                name = "ejb/PhotoGateway",
                beanInterface = PhotoGateway.class),
        @EJB(beanName = "PhotoPaginator",
                name = "ejb/PhotoPaginator",
                beanInterface = PhotoPaginator.class)
})
public class AlbumServlet extends HttpServlet {

    @EJB
    private AlbumDAO albumDAO;
    @EJB
    private Exporter exporter;

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        // Zpřístupníme albumDAO JSP stránkám přes kontext aplikace
        servletConfig.getServletContext().setAttribute("albumDAO", albumDAO);
    }

    private AlbumPageBean getAlbumPageBean(HttpServletRequest request)
            throws ServletException {
        AlbumPageBean albumBean = (AlbumPageBean)
                request.getSession().getAttribute("albumBean");
        if (albumBean == null) {
            return initializeAlbumPageBean(request.getSession());
        }
        return albumBean;
    }

    private AlbumPageBean initializeAlbumPageBean(HttpSession session)
            throws ServletException {
        try {
            Context jndi = new InitialContext();
            // Pro vyhledávání beanu v lokálním ENC se musí použít
            // prefix 'java:comp/env/'. Řetězec 'ejb/PhotoGateway'
            // odpovídá hodnotě atributu name v anotaci EJB na této třídě.
            PhotoPaginator paginator = (PhotoPaginator)
                    jndi.lookup("java:comp/env/ejb/PhotoPaginator");
            AlbumPageBean albumPageBean = new AlbumPageBean(paginator, albumDAO, exporter);
            session.setAttribute("albumBean", albumPageBean);

            return albumPageBean;
        } catch (NamingException e) {
            throw new ServletException(e);
        }
    }

    private void destroyAlbumPageBean(HttpSession session) {
        session.removeAttribute("albumBean");
    }

    @Override
    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response)
            throws ServletException, IOException {
        String action = request.getParameter("action");
        performAction(request, response, action);
    }

    @Override
    protected void doPost(HttpServletRequest request,
                          HttpServletResponse response)
            throws ServletException, IOException {
        String action = readStringParameter(request, "action");
        performAction(request, response, action);
    }

    private void performAction(HttpServletRequest request,
                               HttpServletResponse response,
                               String action) throws IOException, ServletException {
        // V případě, že nebyla specifikována akce,
        // zobrazíme hlavní stránku (upload.jsp).
        if ("".equals(action) || action == null) {
            destroyAlbumPageBean(request.getSession());
            initializeAlbumPageBean(request.getSession());
            request.getRequestDispatcher("upload.jsp").forward(request, response);
            return;
        }

        request.setCharacterEncoding("utf-8");

        // Větvení podle akce
        if ("upload".equals(action)) {
            doUpload(request, response);
        } else if ("detail".equals(action)) {
            doDetail(request, response);
        } else if ("cancel".equals(action)) {
            doCancel(request, response);
        } else if ("resize".equals(action)) {
            doResize(request, response);
        } else if ("save".equals(action)) {
            doSave(request, response);
        } else if ("refresh".equals(action)) {
            doRefresh(request, response);
        } else if ("next".equals(action)) {
            doNext(request, response);
        } else if ("previous".equals(action)) {
            doPrevious(request, response);
        } else if ("first".equals(action)) {
            doFirst(request, response);
        } else if ("last".equals(action)) {
            doLast(request, response);
        } else if ("setPageSize".equals(action)) {
            doSetPageSize(request, response);
        } else if ("selectAlbum".equals(action)) {
            doSelectAlbum(request, response);
        } else if ("removeSelected".equals(action)) {
            doRemoveSelected(request, response);
        } else if ("exportPhotos".equals(action)) {
            doExportPhotos(request, response);
        }

    }

    private void doExportPhotos(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String fromParam = request.getParameter("from");
        String toParam = request.getParameter("to");
        try {
            getAlbumPageBean(request).exportPhotos(fromParam, toParam);
        } catch (ParseException e) {
            throw new ServletException(e);
        }
        request.getRequestDispatcher("upload.jsp").forward(request, response);
    }

    private void doRemoveSelected(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String[] selectedPhotos = request.getParameterValues("selectedPhoto");
        getAlbumPageBean(request).removeSelected(selectedPhotos);
        request.getRequestDispatcher("upload.jsp").forward(request, response);
    }

    private void doSelectAlbum(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String albumName = request.getParameter("selectedAlbum");
        getAlbumPageBean(request).selectAlbum(albumName);
        request.getRequestDispatcher("upload.jsp").forward(request, response);
    }

    private void doNext(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        getAlbumPageBean(request).next();
        request.getRequestDispatcher("upload.jsp").forward(request, response);
    }

    private void doPrevious(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        getAlbumPageBean(request).previous();
        request.getRequestDispatcher("upload.jsp").forward(request, response);
    }

    private void doFirst(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        getAlbumPageBean(request).first();
        request.getRequestDispatcher("upload.jsp").forward(request, response);
    }

    private void doLast(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        getAlbumPageBean(request).last();
        request.getRequestDispatcher("upload.jsp").forward(request, response);
    }

    private void doSetPageSize(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String pageSize = request.getParameter("pageSize");
        getAlbumPageBean(request).setPageSize(Integer.parseInt(pageSize));
        request.getRequestDispatcher("upload.jsp").forward(request, response);
    }

    private void doRefresh(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PhotoGateway gw = getAlbumPageBean(request).getDetailBean().getGateway();
        gw.refresh();
        request.getRequestDispatcher("detail.jsp").forward(request, response);
    }

    private void doSave(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        PhotoGateway gw = getAlbumPageBean(request).getDetailBean().getGateway();
        gw.save();
        // nastavením null se zavírá model detailu 
        getAlbumPageBean(request).setDetailBean(null);

        request.getRequestDispatcher("upload.jsp").forward(request, response);
    }

    private void doResize(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        int width = Integer.parseInt(request.getParameter("width"));
        int height = Integer.parseInt(request.getParameter("height"));
        getAlbumPageBean(request).getDetailBean().getPhoto().resize(width, height);

        request.getRequestDispatcher("detail.jsp").forward(request, response);
    }

    private void doCancel(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        getAlbumPageBean(request).setDetailBean(null);
        request.getRequestDispatcher("upload.jsp").forward(request, response);
    }

    private void doDetail(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        try {
            // Vytvoříme připojení k ENC (JNDI)
            Context jndi = new InitialContext();
            // Pro vyhledávání beanu v lokálním ENC se musí použít
            // prefix 'java:comp/env/'. Řetězec 'ejb/PhotoGateway'
            // odpovídá hodnotě atributu name v anotaci EJB na této třídě.
            PhotoGateway gw = (PhotoGateway) jndi.lookup("java:comp/env/ejb/PhotoGateway");
            String photoName = request.getParameter("photo");
            if ("".equals(photoName)) {
                throw new ServletException("No photo name specified");
            }
            gw.find(photoName);
            // Nastavíme detail bean do album beanu.
            getAlbumPageBean(request).setDetailBean(new PhotoDetailPageBean(gw));

            request.getRequestDispatcher("detail.jsp").forward(request, response);
        } catch (NamingException e) {
            throw new ServletException(e);
        } catch (NoPhotoFoundException e) {
            throw new ServletException(e);
        }
    }

    private void doUpload(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        // Přečteme název, popis a data fotografie
        String name = readPartAsString(request.getPart("name"));
        String description = readPartAsString(request.getPart("description"));
        String albumName = readPartAsString(request.getPart("album"));
        byte[] image = readPartAsBytes(request.getPart("file"));

        // Vytvoříme nové PDO fotografie
        Photo photo = new Photo();
        photo.setName(name);
        photo.setDescription(description);
        photo.setImage(image);

        // Použijeme DAO k uložení fotografie
        albumDAO.addPhoto(photo, albumName);

        // vybereme kontextové album
        getAlbumPageBean(request).selectAlbum(albumName);

        // Přesměrujeme zpět na hlavní stránku
        response.sendRedirect("upload.jsp");
    }


    /**
     * Čte parametr z dotazu jako řetězec. Pokud je dotaz složen
     * z více částí, musíme ho načíst metodou readPartAsString.
     */
    private String readStringParameter(HttpServletRequest request,
                                       String parameter)
            throws IOException, ServletException {

        if (request.getContentType().startsWith("multipart/form-data")) {
            return readPartAsString(request.getPart(parameter));
        } else {
            return request.getParameter(parameter);
        }
    }

    /**
     * Složený dotaz je reprezentován sekvencí objektů Part. Každý
     * objekt odpovídá hodnotě jednoho vstupního pole ve formuláři.
     * Data jsou k dispozici ve formě vstupního proudu, nikoliv
     * jako řetězec.
     */
    private String readPartAsString(Part part) throws IOException {
        Reader reader = new InputStreamReader(
                part.getInputStream(), "utf-8");
        StringBuilder sb = new StringBuilder();

        int c;
        while ((c = reader.read()) != -1) {
            sb.append((char) c);
        }

        return sb.toString();
    }

    /**
     * Podobné metodě readPartAsString, avšak data jsou načítána
     * v binární podobě do pole bytů.
     */
    private byte[] readPartAsBytes(Part part) throws IOException {
        InputStream is = part.getInputStream();
        byte[] bytes = new byte[(int) part.getSize()];
        if (is.read(bytes) != bytes.length) {
            throw new IOException("Truncated part");
        }

        return bytes;
    }

}

